package com.googlecode.dgwt.client.dojo.data.base;

import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;

import com.googlecode.dgwt.client.dojo.data.model.KeyValueColumn;
import com.googlecode.dgwt.client.dojo.data.model.KeyValueEntry;
import com.googlecode.dgwt.client.dojo.data.model.KeyValueModel;
import com.googlecode.dgwt.client.dojo.data.model.StoreModel;

/**
 * @author      Wellington Tadeu dos Santos
 * @since       DGWT v0.01
 */
public class KeyValueStoreBase<Key,Value> extends ModeledStoreBase<Entry<Key,Value>,KeyValueModel<Key, Value>,KeyValueColumn<Key, Value>>  {

    
    public KeyValueStoreBase(Class<Key> keyType, Class<Value> valueType) {
        this(new KeyValueModel<Key, Value>(keyType, valueType));
    }

    public KeyValueStoreBase(Class<Key> keyType, Class<Value> valueType, int initialCapacity) {
        this(new KeyValueModel<Key, Value>(keyType, valueType), initialCapacity);
    }

    public KeyValueStoreBase(Class<Key> keyType, Class<Value> valueType, Map<Key, Value> items) {
        this(new KeyValueModel<Key, Value>(keyType, valueType), items.entrySet());
    }

    public KeyValueStoreBase(Class<Key> keyType, Class<Value> valueType, Collection<? extends Entry<Key, Value>> items) {
        this(new KeyValueModel<Key, Value>(keyType, valueType), items);
    }


    public KeyValueStoreBase(KeyValueModel<Key, Value> model) {
        super(model);
    }

    public KeyValueStoreBase(KeyValueModel<Key, Value> model, int initialCapacity) {
        super(model, initialCapacity);
    }

    public KeyValueStoreBase(KeyValueModel<Key, Value> model, Map<Key, Value> items) {
        super(model, items.entrySet());
    }

    public KeyValueStoreBase(KeyValueModel<Key, Value> model, Collection<? extends Entry<Key, Value>> items) {
        super(model, items);
    }


    protected KeyValueStoreBase(KeyValueStoreBase<Key, Value> mirroredStore, KeyValueModel<Key, Value> newModel) {
        super(mirroredStore, newModel);
    }

    public int indexOfKey(Key key) {
        int index = 0;
        if (key == null) {
            for (Entry<Key,Value> entry : this) {
                if (entry.getKey() == null) {
                    return index;
                }
                index++;
            }
        } else {
            for (Entry<Key,Value> entry : this) {
                if (entry.getKey().equals(key)) {
                    return index;
                }
                index++;
            }
        }
        return -1;
    }

    public int indexOfValue(Value value) {
        int index = 0;
        if (value == null) {
            for (Entry<Key,Value> entry : this) {
                if (entry.getValue() == null) {
                    return index;
                }
                index++;
            }
        } else {
            for (Entry<Key,Value> entry : this) {
                if (entry.getValue().equals(value)) {
                    return index;
                }
                index++;
            }
        }
        return -1;
    }

    public Entry<Key,Value> findByKey(Key key) {
        if (key == null) {
            for (Entry<Key,Value> entry : this) {
                if (entry.getKey() == null) {
                    return entry;
                }
            }
        } else {
            for (Entry<Key,Value> entry : this) {
                if (entry.getKey().equals(key)) {
                    return entry;
                }
            }
        }
        return null;
    }

    
    public Entry<Key,Value> findByValue(Value value) {
        if (value == null) {
            for (Entry<Key,Value> entry : this) {
                if (entry.getValue() == null) {
                    return entry;
                }
            }
        } else {
            for (Entry<Key,Value> entry : this) {
                if (entry.getValue().equals(value)) {
                    return entry;
                }
            }
        }
        return null;
    }

    
    public boolean containsKey(Key key) {
        return findByKey(key) != null;
    }


    public boolean containsValue(Value value) {
        return findByValue(value) != null;
    }

    
    public Entry<Key,Value> add(Key key, Value value) {
        if (isReadOnly()) {
            return null;
        }
        Entry<Key,Value> entry = getModel().createItem(key, value);
        if (!add(entry)) {
            return null;
        }
        return entry;
    }

    
    public Entry<Key,Value> add(int index, Key key, Value value) {
        if (isReadOnly()) {
            return null;
        }
        Entry<Key,Value> entry = getModel().createItem(key, value);
        add(index, entry);
        return entry;
    }

    
    /**
     * Replaces the element at the specified position in this list with the
     * specified element.
     */
    public Entry<Key,Value> set(int index, Key key, Value value) {
        Entry<Key,Value> entry = get(index);
        
        if (entry instanceof KeyValueEntry<?,?>) {
            KeyValueEntry<Key, Value> keyValueEntry = (KeyValueEntry<Key, Value>) entry;
            keyValueEntry.setKey(key);
            keyValueEntry.setValue(value);
            getEngine().set(index, keyValueEntry);
            return entry;
        }
        
        entry = getModel().createItem(key, value);
        
        return set(index, entry);
    }
    
    
    public void addAll(Map<Key,Value> m) {
        addAll(m.entrySet());
    }
    

    public boolean containsAll(Map<Key,Value> c) {
        return super.containsAll(c.entrySet());
    }
    

    public boolean removeAll(Map<Key,Value> c) {
        return super.removeAll(c.entrySet());
    }
    

    public boolean retainAll(Map<Key,Value> c) {
        return super.retainAll(c.entrySet());
    }
    

    @Override
    @SuppressWarnings("unchecked")
    public KeyValueStoreBase<Key, Value> createMirror(StoreModel<Entry<Key, Value>, ?> model) {
        return new KeyValueStoreBase<Key, Value>(this, (KeyValueModel<Key, Value>) model);
    }
    
    
    @Override
    public KeyValueStoreBase<Key, Value> createMirror() {
        return createMirror(getModel());
    }
    

    @Override
    protected Class<?> getModelBaseClass() {
        return KeyValueModel.class;
    }
    
    @Override
    public void setReadOnly(boolean readOnly) {
        super.setReadOnly(readOnly);
    }

    public Entry<Key, Value> addNew(Key key, Value value) {
        if (!isReadOnly()){
            Entry<Key, Value> item = getModel().createItem(key, value);
            if (add(item)){
                return item;
            }
        }
        return null;
    }

}
